package com.tanhua.server.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.dubbo.api.CommentsApi;
import com.tanhua.dubbo.api.MovementApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.VisitorsApi;
import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.domain.Visitors;
import com.tanhua.model.enums.CommentType;
import com.tanhua.model.mongo.Comment;
import com.tanhua.model.mongo.Friend;
import com.tanhua.model.mongo.Movement;
import com.tanhua.model.vo.MovementsVo;
import com.tanhua.model.vo.PageResultVo;
import com.tanhua.model.vo.VisitorsVo;
import com.tanhua.server.exception.BusinessException;
import com.tanhua.server.exception.ErrorResult;
import com.tanhua.server.interceptor.UserHolderThreadLocal;
import com.tanhua.utils.Constants;
import org.apache.dubbo.config.annotation.DubboReference;
import org.bson.types.ObjectId;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: test
 * @Create by: JJ菜菜
 * @Date: 2021/12/1 23:33
 */

@Service
public class MovementService {

    @Autowired
    private OssTemplate ossTemplate;

    @Autowired
    private RedisTemplate redisTemplate;


    @DubboReference
    private MovementApi movementApi;

    @DubboReference
    private UserInfoApi userInfoApi;

    @DubboReference
    private CommentsApi commentsApi;

    @DubboReference
    private VisitorsApi visitorsApi;

    @Autowired
    private AmqpTemplate amqpTemplate;




    /**
     * 添加动态
     * @param movement
     * @param imageContent
     */
    public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException {

        // 判断发布的动态是否为空
        if(StringUtils.isEmpty(movement.getTextContent())){
            throw new BusinessException(ErrorResult.contentError());
        }

        // 将文件上到阿里云oss
        ArrayList<String> medias = new ArrayList<>();
        for (MultipartFile multipartFile : imageContent) {

            String filename = multipartFile.getOriginalFilename();
            InputStream inputStream = multipartFile.getInputStream();
            String uploadUri = ossTemplate.upload(filename, inputStream);
            // 将保存文件的返回路径保存到数组
            medias.add(uploadUri);
        }

        // 获取用户id
        Long userId = UserHolderThreadLocal.getId();

        // 将数据封装到Movement中调用api进行动态保存
        movement.setUserId(userId);
        movement.setMedias(medias);

        movementApi.publish(movement);


        //发送动态审核消息
        amqpTemplate.convertSendAndReceive("tanhua.audit.exchange", "audit.movement",
                movement.getId().toHexString());

    }

    /**
     * 查询个人动态
     * @param page
     * @param pageSize
     * @param userId
     * @return
     */
    public PageResultVo getMyMovement(int page, int pageSize, Long userId) {


        // 调用movementapi分页查询
        PageResultVo vo = movementApi.findMovement(page, pageSize, userId);
        // 如果查询出来的数据为空直接返回
        if(vo == null){
            return vo;
        }

        List<MovementsVo> list = new ArrayList<>();
        // 从pageResult中获取moment集合
        List<Movement> movementList = (List<Movement>) vo.getItems();
        for (Movement movement : movementList) {
            // 获取到userId查询userInfo信息
            Long userId1 = movement.getUserId();
            UserInfo userInfo = userInfoApi.findById(userId1);
            // 封装MovementsVo
            MovementsVo movementsVo = MovementsVo.init(userInfo, movement);
            list.add(movementsVo);
        }

        // 将movementsVo集合数据封装到pageResult中
        vo.setItems(list);
        return vo;
    }

    /**
     * 获取好友动态
     * @param page
     * @param pageSize
     * @return
     */
    public PageResultVo friendMovement(Integer page, Integer pageSize) {

         Long userId = UserHolderThreadLocal.getId();

        // 先查询时间线，再获取moventId查询movent
        List<Movement> movementList =  movementApi.friendMovement(page, pageSize, userId);

        return getPageResultVo(page, pageSize, movementList);

    }

    // 公共方法，好友推荐
    private PageResultVo getPageResultVo(Integer page, Integer pageSize, List<Movement> movementList) {
        if (CollectionUtil.isEmpty(movementList)){
            return new PageResultVo();
        }

        // 使用hutool工具将对象集合转换为属性集合，获取id
        List<Long> userIds = CollectionUtil.getFieldValues(movementList, "userId", Long.class);

        // 根据userId查询userInfo
        Map<Long, UserInfo> map = userInfoApi.findByIds(null, userIds);

        List<MovementsVo> list = new ArrayList<>();
        for (Movement movement : movementList) {
            // 根据动态的userId获取到userInfo
            UserInfo userInfo = map.get(movement.getUserId());

            if(userInfo != null){
                MovementsVo movementsVo = MovementsVo.init(userInfo, movement);
                // 修复点赞状态的bug， 判断redis中的hashkey是否存在
                String key = Constants.MOVEMENTS_INTERACT_KEY + movement.getId().toHexString();
                String hashkey = Constants.MOVEMENT_LIKE_HASHKEY + UserHolderThreadLocal.getId();
                // 如果在redis中存在就将点赞状态设置为1，已经点赞
                if(redisTemplate.opsForHash().hasKey(key, hashkey)){
                    movementsVo.setHasLiked(1);
                }
                list.add(movementsVo);
            }
        }
        PageResultVo vo = new PageResultVo(page, pageSize, 0, list);
        return  vo;
    }

    /**
     * 动态推荐
     * @param page
     * @param pageSize
     * @return
     */
    public PageResultVo findRecommendMovement(Integer page, Integer pageSize) {

        Long userId = UserHolderThreadLocal.getId();
        // 拼接redis的key
        String key = Constants.MOVEMENTS_RECOMMEND + userId;
        String recommendValues = (String) redisTemplate.opsForValue().get(key);

        // 在返回空结果集的时候，使用改静态方法省略了判断，并且不用new对象
        List<Movement> list = Collections.EMPTY_LIST;
        // 判断redis中是否有好友推荐,如果没有就查询随机十条
        if(StringUtils.isEmpty(recommendValues)){
            list =  movementApi.randomMovements(pageSize, userId);
            // 调用公共方法，封装数据返沪
            return getPageResultVo(page, pageSize, list);
        }

        // 如果存在，就根据pid查询
        String[] values = recommendValues.split(",");

        // 判断当前起始页索引数是否小于数长度。
        if((page -1) * pageSize < values.length){
            // 对pid进行流收集处理，使用stream流进行分页查询
            List<Long> pids = Arrays.stream(values)
                    .skip((page - 1) * pageSize)
                    .limit(pageSize)
                    .map(v -> Long.valueOf(v))
                    .collect(Collectors.toList());
            list = movementApi.findMovementByPid(pids);
        }

        return getPageResultVo(page, pageSize, list);

    }

    /**
     * 根据id查询
     * @param movementId
     * @return
     */
    public MovementsVo findById(String movementId) {
        //1、调用api根据id查询动态详情
        Movement movement = movementApi.findById(movementId);
        //2、转化vo对象
        if(movement != null) {
            UserInfo userInfo = userInfoApi.findById(movement.getUserId());
            return MovementsVo.init(userInfo,movement);
        }else {
            return null;
        }
    }


    /**
     * 点赞
     * @param movementId
     */
    public Integer like(String movementId) {

        Long userId = UserHolderThreadLocal.getId();
        // 调用api查询是否点赞
        boolean haslike = commentsApi.hasComment(movementId, userId, CommentType.LIKE);
        // 如果已经点赞就抛出已经点赞异常
        if(haslike){
            throw new BusinessException(ErrorResult.likeError());
        }

        // 保存数据
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(movementId));
        comment.setCommentType(CommentType.LIKE.getType());
        comment.setUserId(UserHolderThreadLocal.getId());
        comment.setCreated(System.currentTimeMillis());

        Integer integer = commentsApi.saveComments(comment);

        // 拼接key将点赞状态存入redis
        String key = Constants.MOVEMENTS_INTERACT_KEY + movementId;
        String hasKey = Constants.MOVEMENT_LIKE_HASHKEY + UserHolderThreadLocal.getId();

        redisTemplate.opsForHash().put(key, hasKey, "1");

        return integer;
    }

    /**
     * 取消点赞
     * @param id
     * @return
     */
    public Integer dislike(String id) {

        // 判断是否点赞
        Long userId = UserHolderThreadLocal.getId();
        boolean hasComment = commentsApi.hasComment(id, userId, CommentType.LIKE);
        if(!hasComment){
            throw new BusinessException(ErrorResult.disLikeError());
        }
        // 如果点赞就直接删除数据
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(id));
        comment.setCommentType(CommentType.LIKE.getType());
        comment.setUserId(UserHolderThreadLocal.getId());
        Integer count =  commentsApi.removeComment(comment);

        // 再将redis中的数据删除掉
        String key = Constants.MOVEMENTS_INTERACT_KEY + id;
        String hashkey = Constants.MOVEMENT_LIKE_HASHKEY + UserHolderThreadLocal.getId();

        redisTemplate.opsForHash().delete(key, hashkey);
        return count;
    }

    /**
     * 动态喜欢
     * @param id
     */
    public Integer love(String id) {

        // 判断用户是否已经喜欢
        boolean hasComment = commentsApi.hasComment(id, UserHolderThreadLocal.getId(), CommentType.LOVE);
        if (hasComment){
            throw new BusinessException(ErrorResult.loveError());
        }

        // 封装数据，保存
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(id));
        comment.setCommentType(CommentType.LOVE.getType());
        comment.setUserId(UserHolderThreadLocal.getId());
        comment.setCreated(System.currentTimeMillis());

        Integer integer = commentsApi.saveComments(comment);
        // 将喜欢数据保存到redis中
        // 动态id键
        String key = Constants.MOVEMENTS_INTERACT_KEY + id;
        // 用户动态喜欢id
        String hashkey = Constants.MOVEMENT_LOVE_HASHKEY + UserHolderThreadLocal.getId();
        // 将值设置为1，已经喜欢
        redisTemplate.opsForHash().put(key, hashkey, "1");
        return integer;
    }

    /**
     * 动态喜欢取消
     * @param movementId
     * @return
     */
    public Integer unlove(String movementId) {

        Long userId = UserHolderThreadLocal.getId();
        // 判断用户是否喜欢，喜欢就抛出异常
        boolean hasComment = commentsApi.hasComment(movementId, userId, CommentType.LOVE);
        if (!hasComment) {
            throw new BusinessException(ErrorResult.disloveError());
        }

        // 删除喜欢数据返回喜欢数量
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(movementId));
        comment.setCommentType(CommentType.LOVE.getType());
        comment.setUserId(userId);
        comment.setCreated(System.currentTimeMillis());
        Integer integer = commentsApi.removeComment(comment);

        // 从redis中删除喜欢状态
        String key = Constants.MOVEMENTS_INTERACT_KEY + movementId;
        String hashkey = Constants.MOVEMENT_LOVE_HASHKEY + userId;
        redisTemplate.opsForHash().delete(key, hashkey, "0");
        return integer;
    }

    /**
     * 谁看过我
     * @return
     */
    public List<VisitorsVo> queryVisitorsList() {

        // 1. 查看访问时间
        String key = Constants.VISITORS_USER;
        String hashKey = String.valueOf(UserHolderThreadLocal.getId());
        String value = (String) redisTemplate.opsForHash().get(key, hashKey);
        // 访问时间
        Long date = StringUtils.isEmpty(value) ? null : Long.valueOf(value);
        // 2. 调用api查询数据列表List<visitor>, 根据访问用户id和当天访问时间
        List<Visitors> visitorsList = visitorsApi.findMyVisitors(date, UserHolderThreadLocal.getId());
        // 3. 提取用户的id
        List<Long> userIds = CollUtil.getFieldValues(visitorsList, "userId", Long.class);
        // 4. 查看用户详情
        Map<Long, UserInfo> map = userInfoApi.findByIds(null, userIds);

        List<VisitorsVo> vos = new ArrayList<>();
        for (Visitors visitors : visitorsList) {

            UserInfo userInfo = map.get(visitors.getUserId());
            if (userInfo != null){
                VisitorsVo vo = VisitorsVo.init(userInfo, visitors);
                vos.add(vo);
            }
        }
        return vos;
    }
}
